namespace Bowlus.FrontEnd
{
    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    using System.Linq;
    using System.Threading;
    using Bowlus.Common;

    using Microsoft.WindowsAzure;
    using Microsoft.WindowsAzure.Diagnostics;
    using Microsoft.WindowsAzure.ServiceRuntime;

    public class WebRole : RoleEntryPoint
    {
        private FrontEndManager frontEndManager = new FrontEndManager();

        public override bool OnStart()
        {
            // Configer Windows Azure Diagnostics
            BowlusHelper.ConfigureLog();

            RoleEnvironment.Changing += this.RoleEnvironmentChanging;

            try
            {
                frontEndManager.Initialize();
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.Message + '\n' + ex.StackTrace);
                throw;
            }

            RoleEnvironment.Changed += this.RoleEnvironment_Changed;

            return base.OnStart();
        }

        public override void Run()
        {
            // Listen to sync request
            var nms = new NotifyModuleServer(delegate(string msg) {
                if (msg.Equals("FrontEndSync"))
                {
                    Thread.Sleep(3000);
                    frontEndManager.Sync();
                }
            });
            nms.startNotifyService();

            // At the same time, keep Syncing but not very often.
            // The WCF messaging between FrontEnd and AppHost are not reliable.
            // This is useful, when the messages between FrontEnd and AppHost fail.
            while (true)
            {
                try
                {
                    Thread.Sleep(120000);
                    frontEndManager.Sync();
                }
                catch (Exception ex)
                {
                    Trace.TraceError(ex.Message + '\n' + ex.StackTrace);
                }
            }
        }

        void RoleEnvironmentChanging(object sender, RoleEnvironmentChangingEventArgs e)
        {
            if (e.Changes.Any(change => change is RoleEnvironmentConfigurationSettingChange))
            {
                e.Cancel = true;
            }

            // Log the Changing Event
            foreach (var change in e.Changes)
            {
                Trace.TraceInformation(string.Format("RoleEnvironmentChanging: {0}", change.ToString()));
            }
        }

        void RoleEnvironment_Changed(object sender, RoleEnvironmentChangedEventArgs e)
        {
            try
            {
                Trace.TraceInformation(string.Format("RoleEnvironment_Changed, count {0}", e.Changes.Count));
                if (e.Changes.Any(chg => chg is RoleEnvironmentTopologyChange))
                {
                    Trace.TraceInformation("RoleEnvironmentTopologyChange");

                    // TODO: We need to change this to first determine if the change was an add/remove and then start polling the service ()
                    // TODO: Every instance may be reporting this change, may need to figure out a tricky way to determine which are the same
                    while (AreAnyRolesInitializing())
                        Thread.Sleep(15000);

                    FrontEndManager.Rebalance();
                    (new NotifyModuleClient()).NotifyAllInstancesInAnotherRole("AppHostSync");
                }
            }
            catch (Exception ex)
            {
                Trace.TraceError(ex.Message + '\n' + ex.StackTrace);
            }
        }

        private static bool AreAnyRolesInitializing()
        {
            try
            {
                var sm = BowlusSettings.GetManagementService();
                var deployment = sm.GetDeploymentBySlot(BowlusSettings.ServiceName, BowlusSettings.DeploymentSlot);

                foreach (var instance in deployment.RoleInstanceList)
                {
                    Trace.TraceInformation(string.Format("Roles Ready ({0})? - Status: {1}", instance.InstanceName, instance.InstanceStatus), "Information");
                    if (instance.InstanceStatus != "Ready")
                        return true;
                }
            }
            catch (Exception)
            {
                return true;
            }

            return false;
        }

    }
}
